{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import chain\n",
    "from collections import defaultdict, Counter\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import seaborn as sns\n",
    "import plotly.express as px\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "from sklearn.calibration import CalibratedClassifierCV\n",
    "from sklearn.preprocessing import StandardScaler, MinMaxScaler\n",
    "from sklearn.multioutput import MultiOutputRegressor\n",
    "from sklearn.metrics import mean_absolute_error\n",
    "\n",
    "from sklearn.svm import SVR\n",
    "from sklearn.ensemble import RandomForestRegressor\n",
    "from xgboost import XGBRegressor\n",
    "from sklearn.linear_model import LinearRegression\n",
    "\n",
    "from utils import split_data_regression, add_coordinates\n",
    "\n",
    "from hyperopt import fmin, tpe, hp, STATUS_OK, Trials\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "from utils import draw_regression_accuracy\n",
    "\n",
    "draw_accuracy = lambda model, params: draw_regression_accuracy(df, 40, 300, 400, 8, model, params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Regression"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data processing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "During classification we have encountered with problem, that information about correct/uncorrect squares are not enough for understanding. More naturaly for localization to see error, as difference between true point and predicted in 2D plain. To do so, we wanted to try Regression based approach, where we will be predicting **x** and **y** coordinates."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To do so we will use the fact, that while collecting data for fingerprinting 10 points where chosen not randomly but with next scheme:\n",
    "\n",
    "![Subsquare division](./photos/fingerprinting_2/sub_square_division.png)\n",
    "\n",
    "as square naturally divides into 9 points, we have done so, and tenth point was just measured a little bit lefter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv('../data/fingerprinting_2.csv', index_col='Unnamed: 0')\n",
    "df = df.reset_index(drop=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "During the data gathering for 2 and 3 squares (s1 and s2) we have lost 1 orientation measurement (100 samples). As each subsequnt measurement is very similar to previous one and we don't use **Orientation** for predicting, then we can just duplicate the last 100 samples to have our data equable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "grouped = df.groupby(\"Square\")\n",
    "\n",
    "point1 = grouped.get_group(\"s1\").iloc[-100:]\n",
    "point1[\"Point\"] = 39\n",
    "point1[\"Orientation\"] = 3.0\n",
    "\n",
    "point2 = grouped.get_group(\"s2\").iloc[-100:]\n",
    "point2[\"Point\"] = 39\n",
    "point2[\"Orientation\"] = 3.0\n",
    "\n",
    "df = df.append([point1, point2]).reset_index(drop=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, to get specific coordinates for each of 10 points in plain (for each we have measured 100 samples in 4 orientations) we will use next info:\n",
    "\n",
    "- size of stool, on which was microcontroller was 40x40 cm\n",
    "- stool was aligned to side of square or placed in center between two sides\n",
    "- the order of points in square:\n",
    "<pre>\n",
    "  1    2    3\n",
    "  8    9 10 4\n",
    "  7    6    5\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Server-RSSI-1</th>\n",
       "      <th>Server-RSSI-2</th>\n",
       "      <th>Server-RSSI-3</th>\n",
       "      <th>Server-RSSI-4</th>\n",
       "      <th>Server-RSSI-5</th>\n",
       "      <th>Square</th>\n",
       "      <th>Point</th>\n",
       "      <th>Orientation</th>\n",
       "      <th>x</th>\n",
       "      <th>y</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>-55</td>\n",
       "      <td>-71</td>\n",
       "      <td>-69</td>\n",
       "      <td>-57</td>\n",
       "      <td>-58</td>\n",
       "      <td>s0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>20</td>\n",
       "      <td>380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>-57</td>\n",
       "      <td>-64</td>\n",
       "      <td>-58</td>\n",
       "      <td>-48</td>\n",
       "      <td>-58</td>\n",
       "      <td>s0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>20</td>\n",
       "      <td>380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>-57</td>\n",
       "      <td>-64</td>\n",
       "      <td>-58</td>\n",
       "      <td>-48</td>\n",
       "      <td>-58</td>\n",
       "      <td>s0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>20</td>\n",
       "      <td>380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>-57</td>\n",
       "      <td>-64</td>\n",
       "      <td>-57</td>\n",
       "      <td>-48</td>\n",
       "      <td>-58</td>\n",
       "      <td>s0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>20</td>\n",
       "      <td>380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>-57</td>\n",
       "      <td>-65</td>\n",
       "      <td>-57</td>\n",
       "      <td>-48</td>\n",
       "      <td>-58</td>\n",
       "      <td>s0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>20</td>\n",
       "      <td>380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47995</th>\n",
       "      <td>-64</td>\n",
       "      <td>-64</td>\n",
       "      <td>-74</td>\n",
       "      <td>-53</td>\n",
       "      <td>-35</td>\n",
       "      <td>s2</td>\n",
       "      <td>39</td>\n",
       "      <td>3.0</td>\n",
       "      <td>254</td>\n",
       "      <td>350</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47996</th>\n",
       "      <td>-63</td>\n",
       "      <td>-65</td>\n",
       "      <td>-72</td>\n",
       "      <td>-53</td>\n",
       "      <td>-35</td>\n",
       "      <td>s2</td>\n",
       "      <td>39</td>\n",
       "      <td>3.0</td>\n",
       "      <td>254</td>\n",
       "      <td>350</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47997</th>\n",
       "      <td>-63</td>\n",
       "      <td>-65</td>\n",
       "      <td>-72</td>\n",
       "      <td>-54</td>\n",
       "      <td>-35</td>\n",
       "      <td>s2</td>\n",
       "      <td>39</td>\n",
       "      <td>3.0</td>\n",
       "      <td>254</td>\n",
       "      <td>350</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47998</th>\n",
       "      <td>-63</td>\n",
       "      <td>-65</td>\n",
       "      <td>-72</td>\n",
       "      <td>-53</td>\n",
       "      <td>-35</td>\n",
       "      <td>s2</td>\n",
       "      <td>39</td>\n",
       "      <td>3.0</td>\n",
       "      <td>254</td>\n",
       "      <td>350</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47999</th>\n",
       "      <td>-63</td>\n",
       "      <td>-64</td>\n",
       "      <td>-73</td>\n",
       "      <td>-54</td>\n",
       "      <td>-35</td>\n",
       "      <td>s2</td>\n",
       "      <td>39</td>\n",
       "      <td>3.0</td>\n",
       "      <td>254</td>\n",
       "      <td>350</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>48000 rows × 10 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "       Server-RSSI-1  Server-RSSI-2  Server-RSSI-3  Server-RSSI-4  \\\n",
       "0                -55            -71            -69            -57   \n",
       "1                -57            -64            -58            -48   \n",
       "2                -57            -64            -58            -48   \n",
       "3                -57            -64            -57            -48   \n",
       "4                -57            -65            -57            -48   \n",
       "...              ...            ...            ...            ...   \n",
       "47995            -64            -64            -74            -53   \n",
       "47996            -63            -65            -72            -53   \n",
       "47997            -63            -65            -72            -54   \n",
       "47998            -63            -65            -72            -53   \n",
       "47999            -63            -64            -73            -54   \n",
       "\n",
       "       Server-RSSI-5 Square  Point  Orientation    x    y  \n",
       "0                -58     s0      0          0.0   20  380  \n",
       "1                -58     s0      0          0.0   20  380  \n",
       "2                -58     s0      0          0.0   20  380  \n",
       "3                -58     s0      0          0.0   20  380  \n",
       "4                -58     s0      0          0.0   20  380  \n",
       "...              ...    ...    ...          ...  ...  ...  \n",
       "47995            -35     s2     39          3.0  254  350  \n",
       "47996            -35     s2     39          3.0  254  350  \n",
       "47997            -35     s2     39          3.0  254  350  \n",
       "47998            -35     s2     39          3.0  254  350  \n",
       "47999            -35     s2     39          3.0  254  350  \n",
       "\n",
       "[48000 rows x 10 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = add_coordinates(df)\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, y_train, X_val, y_val, X_test, y_test = split_data_regression(\n",
    "    df, points_num=40, train_part=0.7, validation_part=0.15, test_part=0.15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To handle, that we need to find **x** and **y** coordinates, we will use MultiOuputRegressor, which will just make one regressor for each target column"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, we wanted to use Decision based regression, as it is very similar to classification (it is actually classifacation), but model will divide continious space on bins by itself, so we don't need to think is it better to have square with 1 m side length or 10 cm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# minimizing distance\n",
    "def model_accuracy(params,model,train_data,train_labels,cv_data,cv_labels):\n",
    "    model = MultiOutputRegressor(model(**params))\n",
    "    model.fit(train_data, train_labels)\n",
    "    preds = model.predict(cv_data)\n",
    "    return {'loss': np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(preds - cv_labels))), 'status': STATUS_OK}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### XGBoost"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%script false --no-raise-error\n",
    "# setting the space\n",
    "max_depth = np.arange(3, 20, dtype=int)\n",
    "sampling_method = ['gradient_based', 'uniform']\n",
    "xgb_fspace = {\n",
    "    'eta':hp.uniform('eta', 0.01, 1),\n",
    "    'gamma':hp.uniform('gamma', 0.01, 1),\n",
    "    'max_depth':hp.choice('max_depth', max_depth),\n",
    "    'min_child_weight': hp.uniform('min_child_weight', 0, 1),\n",
    "    'subsample': hp.uniform('subsample', 0, 1), \n",
    "    'sampling_method': hp.choice('sampling_method',sampling_method)\n",
    "}\n",
    "\n",
    "xgb_accuracy = lambda x:model_accuracy(x,model=XGBRegressor,train_data=X_train,train_labels=y_train,cv_data=X_val,cv_labels=y_val)\n",
    "trials = Trials()\n",
    "best_xgb = fmin(\n",
    "    fn=xgb_accuracy,\n",
    "    space=xgb_fspace,\n",
    "    algo=tpe.suggest,\n",
    "    max_evals=50, trials=trials, rstate=np.random.RandomState(0))\n",
    "best_xgb['sampling_method']=sampling_method[best_xgb['sampling_method']]\n",
    "best_xgb['max_depth']=max_depth[best_xgb['max_depth']]\n",
    "print('Best parameters: ')\n",
    "for param in best_xgb:\n",
    "    print(param, best_xgb[param])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "best_xgb = {\n",
    "    \"eta\": 0.031067719583302122,\n",
    "    \"gamma\": 0.6239296575409805,\n",
    "    \"max_depth\": 13,\n",
    "    \"min_child_weight\": 0.360699687865366,\n",
    "    \"sampling_method\": \"gradient_based\",\n",
    "    \"subsample\": 0.0038370187252436128\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MultiOutputRegressor(estimator=XGBRegressor(base_score=None, booster=None,\n",
       "                                            colsample_bylevel=None,\n",
       "                                            colsample_bynode=None,\n",
       "                                            colsample_bytree=None,\n",
       "                                            eta=0.031067719583302122,\n",
       "                                            gamma=0.6239296575409805,\n",
       "                                            gpu_id=None, importance_type='gain',\n",
       "                                            interaction_constraints=None,\n",
       "                                            learning_rate=None,\n",
       "                                            max_delta_step=None, max_depth=13,\n",
       "                                            min_child_weight=0.360699687865366,\n",
       "                                            missing=nan,\n",
       "                                            monotone_constraints=None,\n",
       "                                            n_estimators=100, n_jobs=None,\n",
       "                                            num_parallel_tree=None,\n",
       "                                            objective='reg:squarederror',\n",
       "                                            random_state=42, reg_alpha=None,\n",
       "                                            reg_lambda=None,\n",
       "                                            sampling_method='gradient_based',\n",
       "                                            scale_pos_weight=None,\n",
       "                                            subsample=0.0038370187252436128,\n",
       "                                            tree_method=None,\n",
       "                                            validate_parameters=False,\n",
       "                                            verbosity=None),\n",
       "                     n_jobs=None)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_xgb = MultiOutputRegressor(XGBRegressor(**best_xgb,random_state=42))\n",
    "\n",
    "model_xgb.fit(X_train, y_train)\n",
    "predicted_xgb = model_xgb.predict(X_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "105.47558197530654"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(predicted_xgb - y_val)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw_accuracy(XGBRegressor, best_xgb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RandomForest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%script false --no-raise-error\n",
    "max_features=np.arange(1,5,dtype=int)\n",
    "n_estimators=np.arange(100,201,50)\n",
    "rf_fspace = {\n",
    "    'max_depth':hp.choice('max_depth', max_depth),\n",
    "    'max_features': hp.choice('max_features', max_features),\n",
    "    'n_estimators': hp.choice('n_estimators',n_estimators )\n",
    "}\n",
    "\n",
    "rf_accuracy = lambda x:model_accuracy(x,model=RandomForestRegressor,train_data=X_train,train_labels=y_train,cv_data=X_val,cv_labels=y_val)\n",
    "trials = Trials()\n",
    "best_rf = fmin(\n",
    "    fn=rf_accuracy,\n",
    "    space=rf_fspace,\n",
    "    algo=tpe.suggest,\n",
    "    max_evals=50, trials=trials, rstate=np.random.RandomState(0))\n",
    "best_rf['max_features']=max_features[best_rf['max_features']]\n",
    "best_rf['n_estimators']=n_estimators[best_rf['n_estimators']]\n",
    "best_rf['max_depth']=max_depth[best_rf['max_depth']]\n",
    "print('Best parameters: ')\n",
    "for param in best_rf:\n",
    "    print(param, best_rf[param])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "best_rf = {\n",
    "    \"max_depth\": 4,\n",
    "    \"max_features\": 4,\n",
    "    \"n_estimators\": 200\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MultiOutputRegressor(estimator=RandomForestRegressor(bootstrap=True,\n",
       "                                                     ccp_alpha=0.0,\n",
       "                                                     criterion='mse',\n",
       "                                                     max_depth=4,\n",
       "                                                     max_features=4,\n",
       "                                                     max_leaf_nodes=None,\n",
       "                                                     max_samples=None,\n",
       "                                                     min_impurity_decrease=0.0,\n",
       "                                                     min_impurity_split=None,\n",
       "                                                     min_samples_leaf=1,\n",
       "                                                     min_samples_split=2,\n",
       "                                                     min_weight_fraction_leaf=0.0,\n",
       "                                                     n_estimators=200,\n",
       "                                                     n_jobs=None,\n",
       "                                                     oob_score=False,\n",
       "                                                     random_state=42, verbose=0,\n",
       "                                                     warm_start=False),\n",
       "                     n_jobs=None)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_rf = MultiOutputRegressor(RandomForestRegressor(**best_rf,random_state=42))\n",
    "\n",
    "model_rf.fit(X_train, y_train)\n",
    "predicted_rf = model_rf.predict(X_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "103.4035601555301"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(predicted_rf - y_val)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw_accuracy(RandomForestRegressor, best_rf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "scaler = MinMaxScaler()\n",
    "numeric = ['Server-RSSI-1', 'Server-RSSI-2', 'Server-RSSI-3', 'Server-RSSI-4','Server-RSSI-5']\n",
    "X_train[numeric] = scaler.fit_transform(X_train[numeric])\n",
    "X_test[numeric] = scaler.transform(X_test[numeric])\n",
    "X_val[numeric] = scaler.transform(X_val[numeric])\n",
    "y_scaler = MinMaxScaler()\n",
    "y_train = y_scaler.fit_transform(y_train)\n",
    "y_val = y_scaler.transform(y_val)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Linear Regression"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MultiOutputRegressor(estimator=LinearRegression(copy_X=True, fit_intercept=True,\n",
       "                                                n_jobs=None, normalize=True),\n",
       "                     n_jobs=None)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_linear = MultiOutputRegressor(LinearRegression(normalize=True))\n",
    "\n",
    "model_linear.fit(X_train, y_train)\n",
    "predicted_linear = model_linear.predict(X_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "110.42504151642284"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(y_scaler.inverse_transform(predicted_linear)-y_scaler.inverse_transform(y_val))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw_accuracy(LinearRegression, {})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### SVM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%%script false --no-raise-error\n",
    "kernel=[\"poly\", \"rbf\"]\n",
    "gamma=['scale', 'auto']\n",
    "svm_fspace = {\n",
    "    'kernel': hp.choice('kernel',kernel),\n",
    "    'gamma': hp.choice('gamma',gamma),\n",
    "    'C':hp.uniform('C', 1, 10),\n",
    "    \n",
    "}\n",
    "\n",
    "svm_accuracy = lambda x:model_accuracy(x,model=SVR,train_data=X_train,train_labels=y_train,cv_data=X_val,cv_labels=y_val)\n",
    "trials = Trials()\n",
    "best_svm = fmin(\n",
    "    fn=svm_accuracy,\n",
    "    space=svm_fspace,\n",
    "    algo=tpe.suggest,\n",
    "    max_evals=10, trials=trials, rstate=np.random.RandomState(0))\n",
    "best_svm['kernel']=kernel[best_svm['kernel']]\n",
    "best_svm['gamma']=gamma[best_svm['gamma']]\n",
    "print('Best parameters: ')\n",
    "for param in best_svm:\n",
    "    print(param, best_rf[param])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "best_svm = {\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MultiOutputRegressor(estimator=SVR(C=1.0, cache_size=200, coef0=0.0, degree=3,\n",
       "                                   epsilon=0.1, gamma='scale', kernel='rbf',\n",
       "                                   max_iter=-1, shrinking=True, tol=0.001,\n",
       "                                   verbose=False),\n",
       "                     n_jobs=None)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_svm = MultiOutputRegressor(SVR(**best_svm))\n",
    "\n",
    "model_svm.fit(X_train, y_train)\n",
    "predicted_svm = model_svm.predict(X_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "109.5013310384061"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(predicted_svm - y_val)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw_accuracy(SVR, {})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Neural Network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Dropout, LSTM, Flatten\n",
    "from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n",
    "from tensorflow.keras.regularizers import l2 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential([\n",
    "    Dense(64, activation='relu', kernel_regularizer=l2(0.001), input_dim=5),\n",
    "    Dropout(0.5),\n",
    "    Dense(64, activation='relu', kernel_regularizer=l2(0.001)),\n",
    "    Dropout(0.5),\n",
    "    Dense(2)\n",
    "])\n",
    "\n",
    "model.compile(loss='mean_squared_error', optimizer='adam')\n",
    "es = EarlyStopping(monitor='val_loss', \n",
    "                   mode='auto', patience=10)\n",
    "checkpoint = ModelCheckpoint(\"best_feed_forward.h5\", monitor='val_loss', verbose=False, save_best_only=True, mode='min')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f441b756340>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.fit(X_train, y_train, validation_data=(X_val, y_val),\n",
    "          epochs=100,\n",
    "          batch_size=120, callbacks=[es, checkpoint], verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100.49935006840333"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# load a saved model\n",
    "from tensorflow.keras.models import load_model\n",
    "model = load_model('best_feed_forward.h5')\n",
    "np.mean(np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, \n",
    "                            np.abs(\n",
    "                                y_scaler.inverse_transform(model.predict(X_val)) - \n",
    "                                y_scaler.inverse_transform(y_val)\n",
    "                            )))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x7f4474181790>"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from utils import cartesian_cross_product\n",
    "mean_res = pd.DataFrame({\"x\": df[\"x\"].values, \"y\": df[\"y\"].values, \"accuracy\": np.apply_along_axis(\n",
    "    lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(y_scaler.inverse_transform(model.predict(scaler.transform(\n",
    "        df[[\"Server-RSSI-\" + str(i) for i in range(1, 6)]].values))) - df[[\"x\", \"y\"]].values))}).groupby([\"x\", \"y\"]).mean().reset_index()\n",
    "\n",
    "coords = mean_res[[\"x\", \"y\"]].values\n",
    "\n",
    "field = np.zeros((400,300))\n",
    "for p in cartesian_cross_product(np.arange(field.shape[1]), np.arange(field.shape[0])):\n",
    "    field[p[1], p[0]] = mean_res.iloc[np.apply_along_axis(lambda x: (x[0]**2 + x[1]**2)**0.5, 1, np.abs(coords - p)).argmin(), 2]\n",
    "\n",
    "field = np.flip(field, 0)\n",
    "fig, ax = plt.subplots(figsize=(10,10))\n",
    "sns.heatmap(field, ax=ax, xticklabels=False,yticklabels=False,cmap=\"coolwarm_r\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
